//
//  TestModel.swift
//  TestSwift
//
//  Created by 小星星吃KFC on 2021/6/8.
//

import Foundation

protocol DefaultValue {
    associatedtype Value: Decodable
    static var defaultValue: Value { get set }
    
}

@propertyWrapper
struct Default<T: DefaultValue> {
    var wrappedValue: T.Value {
        didSet {
            print("wrappedValue- didSet called \(oldValue)")
        }
        willSet {
            print("wrappedValue -willSet called \(newValue)")
        }
    }
    
}

extension Default: Decodable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        wrappedValue = (try? container.decode(T.Value.self)) ?? T.defaultValue
    }
}

extension KeyedDecodingContainer {
    func decode<T>(_ type: Default<T>.Type, forKey key: Key) throws -> Default<T> where T: DefaultValue {
        //判断 key 缺失的情况，提供默认值
        (try decodeIfPresent(type, forKey: key)) ?? Default(wrappedValue: T.defaultValue)
    }
}



extension Int: DefaultValue {
    
    static var defaultValue = -1
    
    struct zero: DefaultValue {
        static var defaultValue = 0
    }
}

extension String {
    
    struct Unknown: DefaultValue {
        
        static var defaultValue = "unknown"
    }
    struct Unnamed: DefaultValue {
        static var defaultValue = "unnamed"
    }
}




func TestModel(){
    
    
    //    let tom = Person(firstName: "Tom", lastName: "Wang", gender: .Male, age: 25, interest: "basketball")
    //    print("person=\(tom)")
    
    
    
    let data = #"{ "name": "jim","date":"2017-08-29 04:00:00.000"}"#
    
    struct Person2: Decodable {
        @Default<String.Unknown> var name: String
        @Default<String.Unnamed> var name2: String
        @Default<Int> var age: Int
        @Default<Int.zero> var age2: Int
        var date: Date
    }
    let formatter = DateFormatter()
    formatter.locale = Locale(identifier: "zh_CN")
    formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
    //    formatter.timeZone = TimeZone.current
    //    formatter.timeZone = TimeZone(identifier:"Asia/Shanghai")
    formatter.timeZone = TimeZone(secondsFromGMT: 3600*8)
    
    //    formatter.dateStyle = DateFormatter.Style.medium
    //    formatter.timeStyle = DateFormatter.Style.medium
    
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .formatted(formatter)
    
    let p = try! decoder.decode(Person2.self, from: data.data(using: .utf8)!)
    print("默认值:name=\(p.name),name2=\(p.name2), age=\(p.age), age2=\(p.age2), age2=\(p.age2), date=\(p.date)")
    
    
    struct Person4: Decodable {
        var name: String = ""
        var name2: String = ""
        var age: Int = 0
    }
    
    
    
    
    do {
        let p = try? JSONDecoder().decode(Person4.self, from: data.data(using: .utf8)!)
        print("Person4:name=\(p?.name),name2=\(p?.name2), age=\(p?.age)")
        
    }
    //Person(_name: Default<Swift.String>(wrappedValue: "unknown"), _age: Default<Swift.Int>(wrappedValue: -1))
    //unknown  -1
    
    UserDefaults.standard.set("tom", forKey: "name")
    
    print( UserDefaults.standard.object(forKey: "name"))
    
}

/// 参数默认值
func show(name : String = "jim") {
    
    print("show=\(name)")
    
}


class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
      
        
        return instance
    }()
}

final class UserInfo: Codable {
    
    ///支持懒加载, 线程安全
    //    static let shared = UserInfo(name:"空", age: 0, sex: .other)
    static let shared: UserInfo = UserInfo()
    private init() {}
    
    var name:String?
    var age :Int?
    
//    enum CodingKeys: String, CodingKey {
//        case name,age
//    }
    
//    init(from decoder: Decoder) throws {
//        print("from====")
//
//        let container = try decoder.container(keyedBy: CodingKeys.self)
//        name = try container.decode(String.self, forKey: .name)
//        age = try container.decode(Int.self, forKey: .age)
//        UserInfo.shared.age = age
//        UserInfo.shared.name = name;
//
//
//
//
//    }
    
}

extension UserInfo: NSCopying {

    func copy(with zone: NSZone? = nil) -> Any {
        print("zone====")
        return self
    }
}


func TestModel2(){
    
    
    let jsonData = """
     {"name":"jim","age":10,}
    """
    let data = jsonData.data(using:.utf8)!;
    
    
    let user :UserInfo = try! JSONDecoder().decode(UserInfo.self, from: data);
    
    print("单例对象 user = \(user.name ?? "name null")")
    print("单例对象 UserInfo.shared = \(UserInfo.shared.name ?? "name null")")
    
    
}

